﻿using System;
using System.Reflection;
using Demo;
using msilgc.common;
using msilgc.runtime;
using msilgc.runtime.Platforms;

namespace driver {
    public class Program {
        static void Main(string[] args) {
            TestMethods.RunMatrixMultiply_CUDA();
            //TestMethods.RunMatrixMultiply_TestEmulator();
            //TestPrime(1000, 75000);
            //TestAddArraysWithBuilder(10, 3);
        }

        private static void TestAddArraysWithBuilder(int start, int length) {
            MethodInfo method = GetMethod(typeof(SampleKernels.Sample1), "Add_Arrays");
            IMethodHandle methodHandle = new Builder().CompileStaticMethod(method, PlatformType.CUDA, false);

            int[] a = new int[length];
            int[] b = new int[length];
            int[] c = new int[length];
            for (int i = 0; i < length; i++) {
                a[i] = start + i;
                b[i] = start + i;
                c[i] = 0;
            }

            methodHandle.Call(new Grain(1, 1, 1), new Grain(1, 1, 1), a, b, c);

            foreach (int i in c) {
                Console.WriteLine(i);
            }

            Console.WriteLine("done.");
            Console.ReadLine();
        }

        private static void TestPrime(int lowCount, int testCount) {
            const string methodName = "TestPrimes_Parallel_EvenWorkload";
            MethodInfo method = GetMethod(typeof(SampleKernels.Sample1), methodName);
            IMethodHandle methodHandle = new Builder().CompileStaticMethod(method, PlatformType.CUDA, false);

            DateTime start = DateTime.Now;
            methodHandle.Compile(); //compile ahead of time
            int compileMilliSeconds = (int)(DateTime.Now - start).TotalMilliseconds;
            Console.WriteLine("compile time: " + compileMilliSeconds + " milliseconds");

            int[] gpuDivisorCount = new int[testCount];
            int[] cpuDivisorCount = new int[testCount];
            for (int i = 0; i < testCount; i++) {
                gpuDivisorCount[i] = 0;
                cpuDivisorCount[i] = 0;
            }

            const int threadCount = 200;

            int dataGrain = testCount/threadCount;
            int stepSize = testCount/(testCount/threadCount);

            start = DateTime.Now;
            Grain threadGrain = new Grain(threadCount, 1, 1);
            for (int i = 0; i < threadCount; i++) {
                threadGrain.X = i;
                SampleKernels.Sample1.TestPrimes_Parallel_EvenWorkload(
                    cpuDivisorCount, lowCount, dataGrain, stepSize, threadGrain);
            }
            int cpuSeconds = (int)(DateTime.Now - start).TotalMilliseconds;
            Console.WriteLine("cpu done.");

            start = DateTime.Now;

            Grain threadDim = new Grain(threadCount, 1, 1);
            Grain blockDim = new Grain(1, 1, 1);

            methodHandle.Call(threadDim, blockDim, gpuDivisorCount, lowCount, dataGrain, stepSize);

            int gpuSeconds = (int)(DateTime.Now - start).TotalMilliseconds;
            Console.WriteLine("gpu done.");

            Console.WriteLine("gpu milli-seconds: " + gpuSeconds);
            Console.WriteLine("cpu milli-seconds: " + cpuSeconds);

            bool pass = true;
            int maxDebug = 20;
            for (int i = 0; i < testCount; i++) {
                if (gpuDivisorCount[i] != cpuDivisorCount[i]) {
                    Console.WriteLine("cpu and gpu don't agree at index " + i + " gpu: " + gpuDivisorCount[i] + " cpu: " + cpuDivisorCount[i]);
                    pass = false;
                    if (--maxDebug == 0) {
                        break;
                    }
                }
            }
            if (pass) {
                Console.WriteLine("GPU and CPU agree on all values");
            }
            Console.ReadLine();
        }

        static MethodInfo GetMethod(Type cls, string name) {
            foreach (MethodInfo method in cls.GetMethods()) {
                if (method.Name.CompareTo(name) == 0) {
                    return method;
                }
            }
            throw new Exception("Cannot find method: '" + name + "'");
        }
    }
}

